
using System;
using System.Xml;
using System.Windows;
using System.Windows.Media;
using System.Collections.Generic;
using MonoDevelop.XamlDesigner.Serialization;
using System.Text;

namespace MonoDevelop.XamlDesigner
{

    /// <summary>
    /// This will manage DOM and avoid to loose data because serializer build tha whole tree!
    /// </summary>
	public class DOMManager
	{
        //TODO use this class to serialize data each time we add something on canvas
        //TODO modify Element method
		public DOMManager ()
		{
            Map = new Dictionary<DependencyObject, XmlElement> ();
		}
        
        private XmlDocument doc;
        private Dictionary<DependencyObject, XmlElement> Map;
        private static Dictionary<Type, DependencyObject> defaultValues = new Dictionary<Type, DependencyObject>();
        
        public void Load(string xamlSource, FrameworkElement rootElement)
        {
            doc.LoadXml(xamlSource);
            CreateMapping (rootElement);
        }

		void CreateMapping (FrameworkElement rootElement)
		{
            MapElement(rootElement, doc.DocumentElement);
		}

		void MapElement (DependencyObject obj, XmlElement element)
		{
            Map.Add (obj, element);
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj) && i < element.ChildNodes.Count; i++)
			{
				DependencyObject childObject = VisualTreeHelper.GetChild(obj, i);
                XmlElement childElement = (XmlElement)element.ChildNodes[i];
                //TODO check if order is keeped!
                MapElement(childObject, childElement);
			}
		}
        //TODO newElement type and not object!
        public void AddDependencyObject(FrameworkElement parent, FrameworkElement newElement)
        {
            if (parent == null)
                return;
            if (newElement == null)
                return;
            
            XmlNode parentElement = doc;
            if (parent != null && Map.ContainsKey(parent))
            	parentElement = Map[parent];
            
			parentElement.AppendChild (SerialiseElement(newElement));
        }

        //this will be notify when modifying an object on wysiwyg!
        public void ModifyDependencyObject(FrameworkElement element)
        {
            //TODO serialize it and replace in doc!            
        }
        
		XmlElement SerialiseElement (FrameworkElement newElement)
		{
            Type baseType = newElement.GetType();
            List<PropertyData> fields = ReflectionHelper.GetProperties(newElement);
            XmlElement result = doc.CreateElement (baseType.Name);
            
            foreach(PropertyData prop in fields)
            {
				DependencyProperty dependencyProperty = prop.Property;
				object value = null;
			 	try {
					value = newElement.GetValue(dependencyProperty);
				}
				catch {
					System.Diagnostics.Debug.WriteLine(string.Format("Error getting value: {0}.{1} on {2}", prop.DeclaringType, prop.ShortName, newElement));
				}
				
				if (value is PointCollection) {
					PointCollection collection = value as PointCollection;
					string[] points = new string[collection.Count];
					for (int i=0; i<collection.Count; i++) {
						Point p = collection[i];
						points[i] = string.Format("{0},{1}", p.X, p.Y);
					}
                    XmlAttribute att = doc.CreateAttribute (prop.ShortName);
                    att.Value = string.Join(" ", points);
					result.Attributes.Append (att);
					continue;
				}
                if (value is SolidColorBrush && (value != null) && newElement.ReadLocalValue(dependencyProperty) != DependencyProperty.UnsetValue)
                {
                    string name = prop.ShortName;
					if(prop.Attached)
                        name = prop.DeclaringType.Name + "." + name;
                    
                    XmlAttribute att = doc.CreateAttribute (name);
                    att.Value = ((SolidColorBrush)value).Color.ToString();
					result.Attributes.Append (att);
                    continue;
                }
				
                if(!(value is DependencyObject) && (value != null) && newElement.ReadLocalValue(dependencyProperty) != DependencyProperty.UnsetValue)
                {
                    string name = prop.ShortName;
					if(prop.Attached)
                        name = prop.DeclaringType.Name + "." + name;
                    
                    XmlAttribute att = doc.CreateAttribute (name);
                    att.Value = value.ToString();
					result.Attributes.Append (att);
                }
            }
            return result;
		}
        
        public string Serialize ()
        {
            return doc.InnerXml;
        }
        
	}
}
